#include "node_basenode.h"
#include "node_rendertarget.h"
#include "node_rootnode.h"

#include "xassert.h"
#include "g_graphics.h"

#include "xge.h"
#include "scene_mgr.h"


namespace xge{
namespace graphics{

  BaseNode::BaseNode():parent_(NULL),scale_(1.0f,1.0f),rotation_(0.0f){
  }

  BaseNode::BaseNode( const TexturePtr& texture , const Rect&  texture_rect ,
    const Point& anchor , const Point& scale , const Point& position, float rotation ):
    texture_(texture),texture_rect_(texture_rect),anchor_(anchor),
    scale_(scale),position_(position),rotation_(rotation),parent_(NULL){
    }

  BaseNode::~BaseNode() {
    if( parent_ != NULL ) {
      parent_->RemoveChildNode(this);
      parent_->Drop();
    }
  }

  BaseNode::G2DRenderState::G2DRenderState( int m , int pt , RenderTargetNode* rt_n , 
    const TexturePtr& t_ptr , int bm , int wtm ) : mode(m) , ptype(pt) , 
    texture_ptr(t_ptr),blending_mode(bm),w_timeout(wtm) {
      if(rt_n)
        rt_n->Grab();
      rt_node = rt_n;
  }

  BaseNode::G2DRenderState::~G2DRenderState() {
    if(rt_node)
      rt_node->Drop();
  }

  // Static function

  int BaseNode::BeginScene( int rendering_mode , int primitive_type , RenderTargetNode* render_target_node , 
    const TexturePtr& texture_ptr , int blending_mode , size_t wait_time_out /* = 100 */ ) {
      if( kStackDepth >0 ) {
        // we do not synchronizing the rendering state
        // the last calling will make it sync  
        XGameEngine::GetGraphics2D()->EndScene(false);
      } 
      // now push the state into the stack 
      kG2DStateStack.push( G2DRenderState(
        rendering_mode , primitive_type , render_target_node , texture_ptr , blending_mode , wait_time_out ));
      kStackDepth++;
      XGameEngine::GetGraphics2D()->BeginScene(
        rendering_mode,primitive_type,render_target_node==NULL ? NULL : render_target_node->render_target_ ,
        texture_ptr.Null() ? NULL : texture_ptr.Get() , blending_mode , wait_time_out );

      return kStackDepth;
  }

  int BaseNode::EndScene() {
    if( kStackDepth >1 ) {
      XGameEngine::GetGraphics2D()->EndScene(false);
    } else if( kStackDepth == 1 ) {
      XGameEngine::GetGraphics2D()->EndScene(true);
      kG2DStateStack.pop();
      --kStackDepth;
      return 0;
    } else {
      SYS_LOG(PANIC,true,_xT("The nested calling of EndScene and BeginScene is not corresponding!"));
      return -1;
    }
    // When reaching here which means we have to resume our states 
    kG2DStateStack.pop();
    G2DRenderState& state = kG2DStateStack.top();
    // Now we have resume the internal Graphics2D state here 
    XGameEngine::GetGraphics2D()->BeginScene(
      state.mode,state.ptype,state.rt_node==NULL ? NULL : state.rt_node->render_target_ ,
      state.texture_ptr.Null() ? NULL : state.texture_ptr.Get() , state.blending_mode , state.w_timeout );
    --kStackDepth;
    return kStackDepth;
  }

  size_t BaseNode::kStackDepth = 0;
  BaseNode::G2DRenderStateStack BaseNode::kG2DStateStack;

  void ListBaseNode::AddChildNode( BaseNode* node ) {
    // 1. check this node is the ListBaseNode or not !!!
    if( GetNodeType() != node->GetNodeType() ) 
      return ;
    // Doing the down case of this node !!! This is safe in theory!
    ListBaseNode* downcast_node = (ListBaseNode*)(node);
    // 1. grab the reference and make sure that the node has been owned
    // by us !
    downcast_node->Grab();
    // 2. release this node from its parents child list 
    if( (downcast_node->parent()) != NULL ) {
      // this node must have been added to the parent node before
      downcast_node->parent()->RemoveChildNode(node);
    } 
    // 3. add this node to our list and make the tag
    NodeIdentity id = child_list_.insert( child_list_.end() , downcast_node );
    downcast_node->id_ = id;
    // 4. change the node parent node 
    downcast_node->set_parent(this);
  }

  ListBaseNode::NodeIdentity ListBaseNode::RemoveChildNodeInternal( BaseNode* node ) {
    // 2. Checking if this node is really in his node list 
    XASSERT( IsThisNodeExistInChildList(node) );
    ListBaseNode* downcast_node = (ListBaseNode*)(node);
    // 1. remove it from our list
    NodeIdentity ret = child_list_.erase( downcast_node->id_ );
    // 2. reduce its node reference count
    downcast_node->Drop();
    // 3. reduce its parent count as well
    // its parent is me !(this)
    downcast_node->set_parent(NULL);
    return ret;
  }

#ifdef _DEBUG
  bool ListBaseNode::IsThisNodeExistInChildList( BaseNode* node ) {
    return node->parent() == this;
  }
#endif 

  // Rendering all its sub child node list 
  void ListBaseNode::OnPaint( const Timer::tick_t& time_diff ) {
    // Rendering all the child node here
    // just tick them 
    RenderingNodeList::iterator ib = child_list_.begin() ,
      ie= child_list_.end();
    for ( ; ib != ie ; ++ib ) {
      (*ib)->OnPaint(time_diff);
    }
  }

  ListBaseNode::ListBaseNode():BaseNode() {}
  ListBaseNode::ListBaseNode(const TexturePtr& texture , const Rect& texture_rect , const Point& anchor , 
    const Point& scale , const Point& position, float rotation )
    :BaseNode(texture,texture_rect,anchor,scale,position,rotation){}


  void ListBaseNode::ClearChildList() {
    // We should remove all our child node here
    RenderingNodeList::iterator ib = child_list_.begin() ,
      ie = child_list_.end();

    for( ; ib != ie ; ) {
      ib = RemoveChildNodeInternal(*ib);
    }
  }

}//namespace graphics
}//namespace xge